package com.shop.cloud.monitor.config;

import de.codecentric.boot.admin.server.config.AdminServerProperties;
import jakarta.servlet.DispatcherType;
import org.springframework.boot.autoconfigure.security.SecurityProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.Customizer;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.provisioning.InMemoryUserDetailsManager;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.authentication.SavedRequestAwareAuthenticationSuccessHandler;
import org.springframework.security.web.authentication.www.BasicAuthenticationFilter;
import org.springframework.security.web.csrf.CookieCsrfTokenRepository;
import org.springframework.security.web.csrf.CsrfTokenRequestAttributeHandler;
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;

import java.util.UUID;

import static org.springframework.http.HttpMethod.DELETE;
import static org.springframework.http.HttpMethod.POST;

/**
 * 监控权限配置
 *
 * @author
 */
@Configuration
public class WebSecurityConfig {

	private final AdminServerProperties adminServer;

	private final SecurityProperties security;

	public WebSecurityConfig(AdminServerProperties adminServer, SecurityProperties security) {
		this.adminServer = adminServer;
		this.security = security;
	}

	@Bean
	protected SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
		SavedRequestAwareAuthenticationSuccessHandler successHandler = new SavedRequestAwareAuthenticationSuccessHandler();
		successHandler.setTargetUrlParameter("redirectTo");
		successHandler.setDefaultTargetUrl(this.adminServer.path("/"));

		http.authorizeHttpRequests((authorizeRequests) -> authorizeRequests //
						.requestMatchers(new AntPathRequestMatcher(this.adminServer.path("/assets/**")))
						.permitAll()
						.requestMatchers(new AntPathRequestMatcher(this.adminServer.path("/actuator/info")))
						.permitAll()
						.requestMatchers(new AntPathRequestMatcher(adminServer.path("/actuator/health")))
						.permitAll()
						.requestMatchers(new AntPathRequestMatcher(this.adminServer.path("/login")))
						.permitAll()
						.dispatcherTypeMatchers(DispatcherType.ASYNC)
						.permitAll() // https://github.com/spring-projects/spring-security/issues/11027
						.anyRequest()
						.authenticated())
				.formLogin(
						(formLogin) -> formLogin.loginPage(this.adminServer.path("/login")).successHandler(successHandler))
				.logout((logout) -> logout.logoutUrl(this.adminServer.path("/logout")))
				.httpBasic(Customizer.withDefaults());

		http.addFilterAfter(new CustomCsrfFilter(), BasicAuthenticationFilter.class) // <5>
				.csrf((csrf) -> csrf.csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse())
						.csrfTokenRequestHandler(new CsrfTokenRequestAttributeHandler())
						.ignoringRequestMatchers(
								new AntPathRequestMatcher(this.adminServer.path("/instances"), POST.toString()), // <6>
								new AntPathRequestMatcher(this.adminServer.path("/instances/*"), DELETE.toString()), // <6>
								new AntPathRequestMatcher(this.adminServer.path("/actuator/**")) // <7>
						));

		http.rememberMe((rememberMe) -> rememberMe.key(UUID.randomUUID().toString()).tokenValiditySeconds(1209600));

		return http.build();

	}

	// Required to provide UserDetailsService for "remember functionality"
	@Bean
	public InMemoryUserDetailsManager userDetailsService(PasswordEncoder passwordEncoder) {
		UserDetails user = User.withUsername(security.getUser().getName())
				.password(passwordEncoder.encode(security.getUser().getPassword()))
				.roles("USER")
				.build();
		return new InMemoryUserDetailsManager(user);
	}

	@Bean
	public PasswordEncoder passwordEncoder() {
		return new BCryptPasswordEncoder();
	}

}
